﻿/**

 * Copyright (c) 2015-2016, FastDev 刘强 (fastdev@163.com).

 *

 * Licensed under the Apache License, Version 2.0 (the "License");

 * you may not use this file except in compliance with the License.

 * You may obtain a copy of the License at

 *

 *      http://www.apache.org/licenses/LICENSE-2.0

 *

 * Unless required by applicable law or agreed to in writing, software

 * distributed under the License is distributed on an "AS IS" BASIS,

 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

 * See the License for the specific language governing permissions and

 * limitations under the License.

 */

using OF.SmartMVC.Core.Compile;
using RazorEngine.Templating;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace OF.SmartMVC.Core
{
    public class CompiledViewBuilder
    {
        private static Type[] emptyTypes = new Type[0];

        private Type rootType = null;
        private string generateAssemblyName = null;
        private List<Tuple<string, Type>> buildParamList = new List<Tuple<string, Type>>();

        public CompiledViewBuilder(Type rootType, string generateAssemblyName)
        {
            this.rootType = rootType;
            this.generateAssemblyName = generateAssemblyName;
        }

        private List<Assembly> GetAllAssemblys()
        {
            Stack<Assembly> assemblyStack = new Stack<Assembly>();
            HashSet<Assembly> assemblyHash = new HashSet<Assembly>();
            List<Assembly> results = new List<Assembly>(100);
            Action<Assembly> initReferencedAssembly = (currentAsm) =>
            {
                var aNames = currentAsm.GetReferencedAssemblies();
                var inits = new List<Assembly>(aNames.Count());
                foreach (var aname in aNames)
                {
                    try
                    {
                        var tas = Assembly.Load(aname);
                        inits.Add(tas);
                    }
                    catch (Exception ex)
                    {
                        System.Diagnostics.Debug.WriteLine(aname + " load failed :" + ex.ToString());
                    }
                }

                foreach (var assembly in inits)
                {
                    if (!assemblyHash.Contains(assembly))
                    {
                        assemblyHash.Add(assembly);
                        assemblyStack.Push(assembly);
                    }
                }
            };
            initReferencedAssembly(rootType.Assembly);
            while (assemblyStack.Count > 0)
            {
                var newAssembly = assemblyStack.Pop();
                results.Add(newAssembly);
                initReferencedAssembly(newAssembly);
            }
            return results;
        }

        public CompiledViewBuilder Add(string template, Type modelType)
        {
            buildParamList.Add(new Tuple<string, Type>(template, modelType));
            return this;
        }

        public void Generate()
        {
            List<Assembly> referenceAssemblies = null;
            referenceAssemblies = GetAllAssemblys();

            string template = null;
            IRazorEngineService razorEngineService = null;

            foreach (var tuple in buildParamList)
            {
                template = tuple.Item1;
                razorEngineService = EngineHelper.CreateEngineService(rootType,
                    referenceAssemblies, template);
                razorEngineService.Compile(template, tuple.Item2);
            }
            StoreCSharpDirectCompilerService.CompileAll(generateAssemblyName);
        }
    }
}
